feat(canvas): Phase 2 — SSE run panel with live streaming#12
Merged
Conversation
Five-part plan covering SSE service layer, RunSlice state machine, run button with validation, run panel with node highlighting, and reconnection with human-in-the-loop resume. Two review passes addressed race conditions (terminal event + onerror, concurrent reconnection), auth via Vite proxy, and Sheet bottom variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 2.1: SSE service layer
- Export apiUrl() from client.ts for shared URL construction
- Rewrite runs.ts: startRun, resumeRun, cancelRun, getRunStatus,
connectStream with typed SSE events and Last-Event-ID support
- All request bodies wrap input in { input: ... } per server schema
Phase 2.2: RunSlice state machine
- Full event dispatch for all 7 GraphEvent types
- terminalReceived flag prevents onerror race after graph_completed
- Module-closure cleanup ref (not in Zustand state)
Phase 2.3: Run button + validation
- Run/Stop button in CanvasHeader with status-driven states
- validateGraph checks Start/End presence, orphans, LLM prompts
- Auto-save before run with error checking
- RunInputDialog for JSON input
Phase 2.4: Run panel + node highlighting
- Sheet extended with side="bottom" (openTransform/closedTransform)
- RunPanel with event timeline, auto-scroll, status banners
- Active node pulse via useNodeId() + useRunStore in BaseNodeShell
Phase 2.5: Reconnection + resume
- Exponential backoff (1s→2s→4s, max 3 attempts)
- Status polling fallback (completed/running/paused/error)
- reconnecting concurrency guard prevents parallel chains
- ResumeForm for human-in-the-loop paused runs
- Fix stale URL in gw-frontend skill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Validate JSON input is a plain object (not string/array/null) - Reset terminalReceived in resumeRun to prevent stale guard - Add Number.isFinite guard on lastEventId query param - Extract duplicate formatDuration to utils/format.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The canvas saves schema_json without the top-level `id` field (it's stored as a separate DB column). But validate_schema requires `id`. Inject graph.id into the schema dict before calling build_graph and validate_schema in both the run and validate endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default state only has a "messages" field. The previous default "result" caused a 422 from the server because the output_key must reference an existing state field. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLM node: when input_map is empty (default), use messages from state instead of building an empty HumanMessage. Gemini rejects empty content with ValueError. Falls back to "Begin." if state messages are also empty. Also include tracebacks in structured JSON logs — the JSONFormatter previously discarded exc_info from logger.exception calls. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
gemini-1.5-flash was retired from the API. Update the default provider to gemini and model to gemini-2.0-flash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace raw node IDs with human-readable labels, add timestamps, show provider/model for LLM nodes, and remove redundant graph_completed row. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace recursive reconnection with iterative loop + .catch() safety net - Fix _handleStreamError type signature (void | Promise<void>) - Add stale-state guards (terminalReceived/reconnecting) after each await - Truncate node output display at 2000 chars to prevent DOM bloat - Reset RunInputDialog state on reopen - Clear ResumeForm input after submit - Update gw-frontend skill with Phase 2 patterns and accurate RunSlice shape Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Screenshot
Key files
packages/canvas/src/api/runs.tspackages/canvas/src/store/runSlice.tspackages/canvas/src/components/panels/RunPanel.tsx,RunEventItem.tsx,ResumeForm.tsxpackages/canvas/src/components/canvas/RunButton.tsx,RunInputDialog.tsxpackages/canvas/src/utils/format.tsTest plan
🤖 Generated with Claude Code